namespace YM {
	// class updateBlock{
	// 	entity:Entity;
	// 	sys:List<any>;
	// 	constructor(entity:Entity,updateSys:List<any>){
	// 		this.entity=entity;
	// 		this.sys=updateSys;
	// 	}
	// }
	export class EventSystem {
		private readonly allComponents: Map<number, Entity> = new Map<number, Entity>();
		private readonly systems: Map<Entity, Entity> = new Map<Entity, Entity>();
		private readonly attributeTypeMap: UnOrderMultiMapSet<Function, { new() }> = new UnOrderMultiMapSet<Function, { new() }>();
		private readonly allEventHandles: Map<string, List<IEventHanddle>> = new Map<string, List<IEventHanddle>>();
		private readonly allEvents: Map<string, Array<any>> = new Map<string, Array<any>>();
		private readonly allOnceEvents: Map<string, List<any>> = new Map<string, List<any>>();

		private readonly awakeSystems: UnOrderMultiMap<{ new() }, IAwakeSystem> = new UnOrderMultiMap<{ new() }, IAwakeSystem>();
		private readonly startSystems: UnOrderMultiMap<{ new() }, IStartSystem> = new UnOrderMultiMap<{ new() }, IStartSystem>();
		private readonly updateSystems: UnOrderMultiMap<{ new() }, IUpdateSystem> = new UnOrderMultiMap<{ new() }, IUpdateSystem>();
		private readonly lateUpdateSystems: UnOrderMultiMap<{ new() }, ILateUpdateSystem> = new UnOrderMultiMap<{ new() }, ILateUpdateSystem>();
		private readonly destroySystems: UnOrderMultiMap<{ new() }, IDestroySystem> = new UnOrderMultiMap<{ new() }, IDestroySystem>();

		private starts: Queue<any> = new Queue<any>();
		private updates: List<any> = new List<any>();
		//private updates2: List<updateBlock> = new List<updateBlock>();
		//private updates3:List<Function>=new List<Function>();
		private lateUpdates: List<any> = new List<any>();
		//private lateUpdates2: List<updateBlock> = new List<updateBlock>();
		private allType: HashSetNoIEC<{ new() }> = new HashSetNoIEC<{ new() }>();
		public init(): void {
			this.allEvents.clear();
			this.allEventHandles.clear();
			for (const eventHandle of eventHandles) {
				this.registerEventHandle(eventHandle.name, new eventHandle.handle());
			}
		}
		public registerEvent(type: string, listener: Function, thisObject: any): void {
			let arr: Array<any> = this.allEvents.get(type);
			if (arr == null) {
				arr = new Array<any>();
				this.allEvents.set(type, arr);
			}
			else {
				for (const element of arr) {
					if (element[0] == listener && element[1] == thisObject) {
						return;
					}
				}
			}
			arr.push([listener, thisObject]);
		}
		public removeEvent(type: string, listener: Function, thisObject: any): void {
			var arr: Array<any> = this.allEvents.get(type);
			if (arr != null) {
				var len = arr.length;
				for (var i = len - 1; i >= 0; i--) {
					if (arr[i][0] == listener && arr[i][1] == thisObject) {
						arr.splice(i, 1);
					}
				}
			}
			if (arr && arr.length == 0) {
				this.allEvents.delete(type);
			}
		}
		public registerOnceEvent(type: string, listener: Function, thisObject: any): void {
			let arr = this.allOnceEvents.get(type);
			if (arr == null) {
				arr = new List<any>();
				this.allOnceEvents.set(type, arr);
			}
			else {
				for (const element of arr) {
					if (element[0] == listener && element[1] == thisObject) {
						return;
					}
				}
			}
			arr.push([listener, thisObject]);
		}
		public removeOnceEvent(type: string, listener: Function, thisObject: any): void {
			var arr: List<any> = this.allOnceEvents.get(type);
			if (arr != null) {
				var len = arr.length;
				for (var i = len - 1; i >= 0; i--) {
					if (arr[i][0] == listener && arr[i][1] == thisObject) {
						arr.splice(i, 1);
					}
				}
			}
			if (arr && arr.length == 0) {
				this.allOnceEvents.delete(type);
			}
		}
		public listenAsync(type:string,thisObject:any){
			return new Promise<void>((reslove,reject)=>{
				this.registerOnceEvent(type,()=>{
					reslove();
				},thisObject);
			});
		}
		public send(type: string, ...args: any[]) {
			if(args){
				if(args.length==1&&(args[0] instanceof Array)){
					args=args[0];
				}
			}
			let arr: Array<any> = this.allEvents.get(type);
			if (arr) {
				arr.forEach(element => {
					element[0].apply(element[1], [type, args]);
					// listener.call(thisObject, args);
				});
			}
			let onceArr = this.allOnceEvents.get(type);
			if (onceArr) {
				onceArr.forEach(element => {
					console.log("EventSystem",type,args);
					(element[0] as Function).apply(element[1], [type, args]);
					// listener.call(thisObject, args);
				});
				this.allOnceEvents.delete(type);
			}
			let handArr = this.allEventHandles.get(type);
			if (handArr) {
				handArr.forEach(element => {
					element.handle(args);
				});
			}
		}
		public registerEventHandle(eventName: string, ievent: IEventHanddle) {
			let events = this.allEventHandles.get(eventName);
			if (events == null) {
				events = new List<IEventHanddle>();
				this.allEventHandles.set(eventName, events);
			}
			events.add(ievent);
		}
		public addSystem(component: Entity){
			if(this.systems.has(component)){
				return;
			}
			if ((<any>component).start.name != "") {
				this.starts.enqueue([(<any>component).start, component]);
			}
			if ((<any>component).update.name != "") {
				this.updates.add([(<any>component).update, component]);
			}
			if ((<any>component).lateUpdate.name != "") {
				this.lateUpdates.add([(<any>component).lateUpdate, component]);
			}
			this.systems.set(component,component);
		}
		public add(component: Entity, isRegister: boolean = true): void {
			if (!isRegister) {
				this.remove(component.instanceId);
				return;
			}
			if (this.allComponents.has(component.instanceId)) {
				console.log("已有ID", component.instanceId);
				return;
			}
			this.allComponents.set(component.instanceId, component);
			this.addSystem(component);
		}
		public removeSystem(component: Entity): void {
			if(!this.systems.has(component)){
				return;
			}
			if ((<any>(component)).start.name != "") {
				let arr = this.starts.toArray();
				let len = arr.length;
				for (var i = len - 1; i >= 0; i--) {
					const element = arr[i];
					if (element[0] == (<any>(component)).start && element[1] == component) {
						arr.splice(i, 1);
					}
				}
			}
			if ((<any>(component)).update.name != "") {
				let arr = this.updates;
				let len = arr.length;
				for (var i = len - 1; i >= 0; i--) {
					const element = arr[i];
					if (element[0] == (<any>(component)).update && element[1] == component) {
						arr.splice(i, 1);
					}
				}
			}
			if ((<any>(component)).lateUpdate.name != "") {
				let arr = this.lateUpdates;
				let len = arr.length;
				for (var i = len - 1; i >= 0; i--) {
					const element = arr[i];
					if (element[0] == (<any>(component)).lateUpdate && element[1] == component) {
						arr.splice(i, 1);
					}
				}
			}
			this.systems.delete(component);
		}
		public remove(instanceId: number): void {
			let component = this.allComponents.get(instanceId);
			if (component != null) {
				this.removeSystem(component);
				this.allComponents.delete(instanceId);
			}
		}

		public get(instanceId: number): Entity {
			let component = this.allComponents.get(instanceId);
			return component;
		}

		public isRegister(instanceId: number) {
			return this.allComponents.has(instanceId);
		}
		public awake(component: Entity): void;
		public awake<A>(component: Entity, a: A): void;
		public awake<A, B>(component: Entity, a: A, b: B): void;
		public awake<A, B, C>(component: Entity, a: A, b: B, c: C): void;
		public awake<A, B, C>(component: Entity, a?: A, b?: B, c?: C): void {
			if ((<any>component).awake && !a && !b && !c) {
				(<any>component).awake();
			}
			else if ((<any>component).awake && a && !b && !c) {
				(<any>component).awake(a);
			}
			else if ((<any>component).awake && a && b && !c) {
				(<any>component).awake(a, b);
			}
			else if ((<any>component).awake && a && b && c) {
				(<any>component).awake(a, b, c);
			}
		}
		public start(): void {
			while (this.starts.count > 0) {
				let startFunc = this.starts.dequeue();
				startFunc[0].call(startFunc[1]);
			}
		}
		public update(dt:number): void {
			this.start();
			let arr = this.updates;
			for (let i = 0; i < this.updates.size; i++) {
				arr[i][0].call(arr[i][1],dt);
			}
		}
		public lateUpdate(): void {
			let arr = this.lateUpdates;
			for (let i = 0; i < this.lateUpdates.size; i++) {
				arr[i][0].call(arr[i][1]);
			}
		}
		public destroy(component: Entity) {
			if ((<any>component).destroy) {
				(<any>component).destroy();
			}
		}
	}
}